home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
drivers1.zip
/
EXP16.ASM
< prev
next >
Wrap
Assembly Source File
|
1992-01-23
|
64KB
|
2,916 lines
version equ 3
DEBUG equ 0
;History:1518,1
; Copyright 1991 Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
include defs.asm
code segment word public
assume cs:code, ds:code
;
; Waits for SCB command unit to become idle
;
; MUST NOT TRASH CX OR THE ISSUE_COMMAND PROCEDURE WILL FAIL
deb macro xxx
if DEBUG
push bx
push ds
mov bx,0b800h
mov ds,bx
mov bx,word ptr xxx
add bx,40
shl bx,1
shl bx,1
inc byte ptr ds:[bx]
pop ds
pop bx
endif
endm
cmd_clear macro
local exit
lea dx, [bp].@SCB_CMD ;BP -> I/O Base
in ax, dx ;Read SCB command
or ax, ax ;Wait for command accepted
jz exit
call cmd_wait
exit:
endm
;
; Drives channel attention to the 586. We don't care whats in AX, the write
; will cause the ASIC to drive CA to the 586.
;
issue_ca MACRO
lea dx, [bp].@CA_Ctrl
out dx, al
endm
;
;
; Enable and disable barts interrupts.
;
enable_board_ints macro
lea dx, [bp].@Sel_IRQ
mov al, encoded_int_no
or al, 08h
out dx, al
endm
disable_board_ints macro
lea dx, [bp].@Sel_IRQ
mov al, encoded_int_no
out dx, al
endm
RxBufferSize equ 1518+14+18 ;Max Rx packet+MAC header+
; 18 (alignment)
TxBufferSize equ 1518+14+20 ;Max Tx packet+MAC header+
; 20 (alignment)
; 82586 Structure definitions
SysConfigPtr struc
SCP_SystemBus dw ?
dw 2 dup (?)
SCP_ISCP_Ptr_low dw ?
SCP_ISCP_Ptr_high dw ?
SysConfigPtr ends
;----------------------------------------------------------------
; Note: The @ addresses are the I/O address offsets from [bp].
;----------------------------------------------------------------
; The SCP is at location XXX6H. Since the
; SMB pointers must have the low order 5 bits
; zero, we assume that the SMP pointer will
; be set to XXE0H
@SysConfigPtr struc
db 8000h dup (?)
dw ?
dw ?
dw ?
@SCP_SystemBus dw ?
dw 2 dup (?)
@SCP_ISCP_Ptr_low dw ?
@SCP_ISCP_Ptr_high dw ?
@SysConfigPtr ends
ISysConfigPtr struc
ISCP_Busy dw ?
ISCP_SCB_Offset dw ?
ISCP_Ptr_low dw ?
ISCP_Ptr_high dw ?
ISysConfigPtr ends
; NOTE: Access to the SCB through the shadow
; registers is done by setting the SMB_PTR to
; the ISCP base address and then using this
; structure. This is because the SCB address
; does not start on a 32 byte boundary as
; as required for the SMB_PTR.
@ISysConfigPtr struc
db 4000h dup (?)
@ISCP_Busy dw ?
@ISCP_SCB_Offset dw ?
@ISCP_SCB_Ptr_low dw ?
@ISCP_SCB_Ptr_high dw ?
@SCB_Status dw ?
@SCB_Command dw ?
@SCB_CommandList dw ?
@SCB_RecBlockList dw ?
db 4000h - 16 dup (?)
@SCB_CRC_Errors dw ?
@SCB_ALN_Errors dw ?
@SCB_RSC_Errors dw ?
@SCB_OVR_Errors dw ?
@ISysConfigPtr ends
SystemControlBlock struc
SCB_Status dw ?
SCB_Command dw ?
SCB_CommandList dw ?
SCB_RecBlockList dw ?
SCB_CRC_Errors dw ?
SCB_ALN_Errors dw ?
SCB_RSC_Errors dw ?
SCB_OVR_Errors dw ?
SystemControlBlock ends
; NOTE: The SCB shadow register structure is
; part of the ISysConfigPtr shadow register
; structure.
ReceiveBlock struc
FD_Status dw ?
FD_Command dw ?
FD_Link dw ?
FD_RBD_Offset dw ?
RBD_ByteCount dw ?
RBD_Link dw ?
RBD_RB_Address_low dw ?
RBD_RB_Address_high dw ?
RBD_Size dw ?
RB_Data db RxBufferSize dup (?)
ReceiveBlock ends
ReceiveBlock_data struc
IPX_FD_Status dw ?
IPX_FD_Command dw ?
IPX_FD_Link dw ?
IPX_FD_RBD_Offset dw ?
IPX_RBD_ByteCount dw ?
IPX_RBD_Link dw ?
IPX_RBD_RB_Address_low dw ?
IPX_RBD_RB_Address_high dw ?
IPX_RBD_Size dw ?
IPX_DestAddr_HIGH dw ?
IPX_DestAddr_MID dw ?
IPX_DestAddr_LOW dw ?
IPX_SourceAddr_HIGH dw ?
IPX_SourceAddr_MID dw ?
IPX_SourceAddr_LOW dw ?
IPX_LengthTypeField dw ?
ReceiveBlock_data ends
@ReceiveBlock struc
db 4000h dup (?)
@FD_Status dw ?
@FD_Command dw ?
@FD_Link dw ?
@FD_RBD_Offset dw ?
@RBD_ByteCount dw ?
@RBD_Link dw ?
@RBD_RB_Address_low dw ?
@RBD_RB_Address_high dw ?
db 4000h - 16 dup (?)
@RBD_Size dw ?
; Ethernet Packet Data
@DestAddr_HIGH dw ?
@DestAddr_MID dw ?
@DestAddr_LOW dw ?
@SourceAddr_HIGH dw ?
@SourceAddr_MID dw ?
@SourceAddr_LOW dw ?
@LengthTypeField dw ?
@ReceiveBlock ends
SendBlock struc
TxCB_Status dw ?
TxCB_Command dw ?
TxCB_Link dw ?
TxCB_TBD_Offset dw ?
TBD_ByteCount dw ?
TBD_Link dw ?
TBD_TB_Address_low dw ?
TBD_TB_Address_high dw ?
TB_Data db TxBufferSize dup (?)
SendBlock ends
@SendBlock struc
db 4000h dup (?)
@TxCB_Status dw ?
@TxCB_Command dw ?
@TxCB_Link dw ?
@TxCB_TBD_Offset dw ?
@TBD_ByteCount dw ?
@TBD_Link dw ?
@TBD_TB_Address_low dw ?
@TBD_TB_Address_high dw ?
db 4000h - 16 dup (?)
@TB_Data db TxBufferSize dup (?)
@SendBlock ends
CommandBlock struc ;Set command block
CB_Status dw ? ; Size for largest
CB_Command dw ? ; command. The Dump
CB_Link dw ? ; command needs a 170
CB_Param0 dw ? ; byte area. The 14
db 170+14 dup (?) ; extra bytes are
CommandBlock ends ; padding to make the
; size of this struc
; boundary.
@CommandBlock struc
db 4000h dup (?) ;Set command block
@CB_Status dw ? ; Size for largest
@CB_Command dw ? ; command. The Dump
@CB_Link dw ? ; command needs a 170
@CB_Parm0 dw ? ; byte area. The 14
db 170+14 dup (?) ; extra bytes are
@CommandBlock ends ; padding to make the
; size of this struc
; align on a 16 byte
SCPS equ SysConfigPtr
ISCPS equ ISysConfigPtr
SCBS equ SystemControlBlock
; System ControlBlock.
Bit_CX equ 8000h
Bit_FR equ 4000h
Bit_CNA equ 2000h
Bit_RNR equ 1000h
; System Control Block's control unit statuses
CUS_Idle equ 0000h
CUS_Suspended equ 0100h
CUS_Active equ 0200h
CUS_Mask equ 0300h
; System Control Block's receive unit statuses.
RUS_Idle equ 0000h
RUS_Suspend equ 0010h
RUS_No_Resource equ 0020h
RUS_Ready equ 0040h
; System Control Block's control unit commands
CUC_NOP equ 0000h
CUC_Start equ 0100h
CUC_Resume equ 0200h
CUC_Suspend equ 0300h
CUC_Abort equ 0400h
; System Control Block's receive unit commands
RUC_NOP equ 0000h
RUC_Start equ 0010h
RUC_Resume equ 0020h
RUC_Suspend equ 0030h
RUC_Abort equ 0040h
; General Action Command Block.
GA_NOP equ 0000h
GA_IA_Setup equ 0001h
GA_Configure equ 0002h
GA_MC_Setup equ 0003h
GA_Transmit equ 0004h
GA_TDR equ 0005h
GA_Dump equ 0006h
GA_Diagnose equ 0007h
lock_bit_address EQU 01H
lock_bit_mask EQU 0000000000000001B
iochrdy_mask EQU 00010000B
iochrdy_test_mask EQU 00100000B
iochrdy_test_result EQU 01000000B
iochrdy_early EQU 00000000B
iochrdy_late EQU 00010000B
; Common Bits.
Bit_C equ 8000h ; Command complete.
Bit_B equ 4000h ; Busy executing command.
Bit_OK equ 2000h ; Error free completion.
Bit_A equ 1000h ; Command aborted.
Bit_EL equ 8000h ; End of list.
Bit_EOF equ 8000h ; End of frame.
byte_Bit_EOF equ 80h ; End of frame.
Bit_S equ 4000h ; Suspend after completion.
Bit_I equ 2000h ; Interrupt after completion.
Bit_S11 equ 0800h
Bit_S10 equ 0400h
Bit_S9 equ 0200h
Bit_S8 equ 0100h
Bit_S7 equ 0080h
Bit_S6 equ 0040h
Bit_S5 equ 0020h
ACK_INT_Mask equ 0f000h
MaxCollisions equ 0020h
Ready equ 0040h
LNK_OK equ 8000h
XCVR_PRB equ 4000h
ET_OPN equ 2000h
ET_SRT equ 1000h
TDR_TIME equ 07ffh
;
; equates and definitions:
;
EOI equ 020h ;End-of-interrupt command.
Master_PIC equ 020h ;1st Interrupt-Control port.
Slave_PIC equ 0A0h ;2nd Interrupt-Control port.
Bart_Board_ID equ 0BABAH
Number_of_Tx_Buffers equ 1
_586_Reset equ 0080H
ASIC_Reset equ 0040H
bus_width equ 0 ;16 bit, use for initialize
initialize_586 equ 1
;
; Timer equates
;
one_mil EQU 02387
ten_mils EQU 23866
;
; EEPROM equATES
;
EE_SK equ 00000001B ;EEPROM shift clock (1 = high, 0 = low)
EE_CS equ 00000010B ;EEPROM chip select (1 = high, 0 = low)
EE_DI equ 00000100B ;EEPROM data in (set to 1 for writing data to EEPROM)
EE_DO equ 00001000B ;EEPROM data out
EE_tick equ 10
EEPROM_read_opcode equ 110B
EEPROM_write_opcode equ 101B
EEPROM_erase_opcode equ 111B
EEPROM_EWEN_opcode equ 10011B ;Erase/write enable
EEPROM_EWDS_opcode equ 10000B ;Erase/write disable
TIME_OUT_VALUE equ 0FFFFh
loopback_enable equ 00000010B
_64K_not_32K db 0 ;<>0 if we have 64K of memory.
_16_not_8_bit_slot db 0 ;<>0 if we're in a 16-bit slot.
connection_type db ?
;Memory Sizes
mem_size_address equ 00H
_32K equ 00000h
_64K equ 00001h
connection_address equ 00H
connection_field equ 0001000000000000B
TPE_address equ 05H
TPE_type_field equ 0000000000000001B
BNC equ 00000h
AUI equ 00001h
TPE equ 00002h
int_num_address equ 00H
int_num_field equ 1110000000000000B
int_field_shift equ 13
ee_ethernet_add_low equ 2
ee_ethernet_add_mid equ 3
ee_ethernet_add_high equ 4
ee_int equ 0
ee_shift equ 13
; CPU types
_8088_CPU equ 00000h
Not_8088_CPU equ NOT _8088_CPU
; Slot Width
slot_width_mask equ 04h
_16_bit_slot equ 0000h
_8_bit_slot equ 0001h
_16_bit_override_bit equ 08h
;
; BART base port structure
;
@BARTBasePorts struc
@Data_Reg dw ? ;Data Transfer Register.
@Write_Ptr dw ? ;Write Address Pointer.
@Read_Ptr dw ? ;Read Address Pointer.
@CA_Ctrl db ? ;Channel Attention Control.
@Sel_IRQ db ? ;IRQ Select.
@SMB_Ptr dw ? ;Shadow Memory Bank Pointer.
db ?
@MEM_Ctrl db ?
@MEM_Page_Ctrl db ?
@Config db ?
@EEPROM_Ctrl db ?
@ID_Port db ?
@BARTBasePorts ends
@ISR_Ports struc
db 0C008h dup(?)
@SCB_STAT dw ?
@SCB_CMD dw ?
@SCB_CBL dw ?
@SCB_RFA dw ?
; @SCB_CRCERRS dw ?
; @SCB_ALNERRS dw ?
; @SCB_RSCERRS dw ?
; @SCB_OVRNERRS dw ?
@ISR_Ports ends
@memory_page_struc STRUC
DB 4000H DUP (?)
@mem_loc_0 DW ?
@mem_loc_2 DW ?
@mem_loc_4 DW ?
@mem_loc_6 DW ?
@mem_loc_8 DW ?
@mem_loc_10 DW ?
@mem_loc_12 DW ?
@mem_loc_14 DW ?
DB 4000H - 16 DUP (?)
@mem_loc_16 DW ?
@mem_loc_18 DW ?
@mem_loc_20 DW ?
@mem_loc_22 DW ?
@mem_loc_24 DW ?
@mem_loc_26 DW ?
@mem_loc_28 DW ?
@mem_loc_30 DW ?
@memory_page_struc ENDS
;
;
; TData Segment
;
; This segment template represents the 64-KB segment that the 82586
; can address. Shared memory can be either 32K or 64K for the BART.
; All structures must be quad-word aligned.
;
; Offset Type of Block Block Size Cnt
; ------ ------------- ---------- ---
; 0000h ISCP 8 1
; 0008h SCB 16 1
; 0020h CommandBlock 104 1
; 00E0h SendBlock(s) 1568 variable
; ????h ReceiveBlock(s) 1568 variable
; FFF6h SCP 10 1
;
TData_64 segment at 0
ISCP ISysConfigPtr <>
SCB SystemControlBlock <>
ORG 20H
CB CommandBlock <>
Send_Blocks DB SIZE SendBlock * Number_of_Tx_Buffers dup (?)
Receive_Blocks LABEL WORD
ORG (0FFFFh - SIZE SCPS + 1) AND 0FFE0H
SCP LABEL BYTE
ORG 0FFFFh - SIZE SCPS + 1
THeapTop LABEL BYTE ;Top of available memory
end_of_send_blocks = 20h + SIZE CommandBlock + (Number_of_Tx_Buffers * SIZE SendBlock)
num_rx_buf_32k = (07FFFh - SIZE SCPS + 1 - end_of_send_blocks) / (SIZE ReceiveBlock)
num_rx_buf_64k = (0FFFFh - SIZE SCPS + 1 - end_of_send_blocks) / (SIZE ReceiveBlock)
TData_64 ENDS
public int_no
int_no db 3,0,0,0 ;must be four bytes long for get_number.
io_addr dw 0,0 ; I/O address for card
encoded_int_no db ? ;encoded for bart.
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 255 ;from the packet spec
driver_name db 'EtherExpress16',0 ;name of the driver.
driver_function db 26
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
save_err db 0 ;error bits on promiscuous recieve
pro db 0
pro1 db 0
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw 0
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4 ;haven't set up perfect filtering yet.
dw 0
dw rcv_mode_6
;
; 82586 Configuration Parameters
;
config_params LABEL WORD
num_config_params LABEL BYTE ;12 BYTEs
DB 0CH
fifo_limit LABEL BYTE ;Default is 8
DB 08H
srdy LABEL BYTE ;srdy = 1
save_bad_frame LABEL BYTE ;do not save bad frame
DB 40H
address_length LABEL BYTE ;6 BYTEs
auto_insert_address LABEL BYTE ;auto insert off (1)
preamble_length LABEL BYTE ;2 BYTEs
internal_loopback LABEL BYTE ;0 = OFF 1 = ON
external_loopback LABEL BYTE ;0 = OFF 1 = ON
DB 2EH
linear_priority LABEL BYTE ;default is 0
accel_contention_resolution LABEL BYTE ;default is 0
exp_backoff_method LABEL BYTE ;0 = 802.3 1 = Alternate
DB 00H
interframe_spacing LABEL BYTE ;default is 60H (96 bits)
DB 60H
slot_time_low LABEL BYTE ;0
DB 00H
slot_time_high LABEL BYTE ;2
retry_num LABEL BYTE ;15
DB 0F2H
promiscuous_mode LABEL BYTE ;0 = OFF 1 = ON
broadcast_disable LABEL BYTE ;0 = OFF 1 = ON
encode_decode LABEL BYTE ;0 = NRZ 1 = MANCHESTER
transmit_on_no_carrier LABEL BYTE ;0 = STOP 1 = CONTINUE
no_crc_insertion LABEL BYTE ;0 = OFF 1 = ON
crc_type LABEL BYTE ;0 = 32 bit autodin II
;1 = 16 bit CCITT
bit_stuffing LABEL BYTE ;0 = 802.3 1 = HDLC
padding LABEL BYTE ;0 = OFF 1 = ON
DB 00H
carrier_sense_filter LABEL BYTE ;0 = OFF 1 = 0N
carrier_sense_source LABEL BYTE ;0 = external 1 = internal
collision_detect_filter LABEL BYTE ;default = 0
collision_detect_source LABEL BYTE ;0 = external 1 = internal
DB 00H
min_frame_length LABEL BYTE ;60 BYTEs
DB 60
DB 00H ;undefined
RxBufferCount dw ?
TxBufferCount dw ?
Receive_Head DW ?
Receive_Tail DW ?
our_type dw 8 dup(?)
is_186 db 0 ;=0 if 808[68], =1 if 80[123]86.
rom_address db EADDR_LEN dup(?)
include io16.asm
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
public send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
; (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;if we're a high-performance driver, es:di -> upcall.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
cmp pro1,1
je no_size_check1
cmp cx,GIANT ; Is this packet too large?
jna no_size_check1
jmp send_pkt_toobig
no_size_check1:
push bp
mov bp,io_addr
disable_board_ints
cmd_clear ; wait for command processor to clear
cmp pro1,1 ; on pro drive do not ajust size
je oklen
cmp cx,RUNT ; minimum length for Ether
jae oklen
mov cx,RUNT ; make sure size at least RUNT
oklen:
inc cx ;round size up to next even number.
and cx,not 1
;
; Set SMB pointer to the transmit buffer.
;
lea dx,[BP].@SMB_Ptr
mov ax,offset send_blocks
out dx,ax
;
; Write evenized, padded packet length (>= RUNT bytes) to 586 TBD byte
; count field. OR on the End of Frame bit.
;
lea dx,[BP].@TBD_ByteCount
mov ax,cx
or ah,byte_BIT_EOF
out dx,ax
;
; Set the TxCB command field for transmit. Also set the EL bit (End of List).
;
lea dx, [BP].@TxCB_Command
mov ax, BIT_EL+GA_Transmit
out dx, ax
;
; Set write pointer to the data buffer for current send block. Also
; set DX to the data register.
;
lea dx, [BP].@Write_Ptr
mov ax, offset send_blocks
add ax, offset TB_Data
out dx, ax
mov dx, bp ;@Data_Reg
call repoutsw
;.286
; rep outsb
send_continue:
;
; Set the SCBs command block pointer to this send packet.
;
lea dx, [BP].@SCB_CBL
mov ax, offset send_blocks
out dx, ax
;
; Set the SCB command to start the command unit.
;
lea dx, [BP].@SCB_CMD
mov ax, CUC_START
out dx, ax
issue_ca
cmd_clear ; wait for frame transmit to start
lea dx,[BP].@SMB_Ptr
mov ax,offset send_blocks
out dx,ax
lea dx,[BP].@TxCB_status
transmiting:
in ax,dx
test ax,BIT_C ; check status of transmit
jz transmiting
test ax,BIT_OK ; how was the x-mit
jnz tx_ok
enable_board_ints
pop bp
mov dh,255
jmp tx_bad
tx_ok:
enable_board_ints
pop bp
clc
ret
send_pkt_toobig:
mov dh,NO_SPACE
tx_bad:
stc
ret
; ReadTickCounter
;
; Read the 16 bit timer tick count register (system board timer 0).
; The count register decrements by 2 (even numbers) every 838ns.
;
; Assumes: Interrupts disabled
;
; Returns: AX with the current count
; Interrupts disabled
ReadTickCounter:
push dx
mov dx, 43h ;Command 8254 timer to latch
xor al, al ; T0's current count
out dx, al
mov dx, 40h ;read the latched count
in al, dx ; LSB first
mov ah, al
in al, dx ; MSB next
xchg al, ah ;put count in proper order
pop dx
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
; Give caller the one currently in the 8390, not necessarily the one in PROM.
assume ds:code
cmp cx, EADDR_LEN ; Caller wants a reasonable length?
jb get_addr_x ; No, fail.
mov cx, EADDR_LEN ; Move one ethernet address from our copy
mov si, offset rom_address ; Copy from most recent setting
rep movsb
mov cx, EADDR_LEN ; Tell caller how many bytes we fed him
clc ; Carry off says success
ret
get_addr_x:
stc ; Tell caller our addr is too big for him
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
assume ds:nothing
cmp cx,EADDR_LEN ;ensure that their address is okay.
je set_address_4
mov dh,BAD_ADDRESS
stc
jmp short set_address_done
set_address_4:
push bp
push ds
push si
push cs
pop ds
;
; Set up individual address command block.
;
mov bp,ds:io_addr
cmd_clear
mov bx, BIT_EL+GA_IA_Setup
call setup_command_block
pop si
pop ds
;
; Copy individual address command parameters to individual address
; command block.
;
mov cx, EADDR_LEN/2
move_node_address:
lodsw
out dx, ax
loop move_node_address
;
; Signal 586 to execute individual address command.
;
mov ax, CUC_Start
mov bx, BIT_CNA
call issue_command
lea dx, [bp].@SMB_Ptr ;Move IO frame to the command
mov ax, offset cb ; command block.
out dx, ax
lea dx,[BP].@CB_Status
working1:
in ax,dx
test ax,BIT_C ; check status of set_ia
jz working1
test ax,BIT_OK
jnz set_ok
pop bp
stc
jmp set_address_done
set_ok:
mov cx,5000h ; on 486's if we do not delay this command does not work
set1:
mov bx,cx
mov ax,[bx]
loop set1 ; just for delay
mov cx,EADDR_LEN ;return their address length.
clc
pop bp
set_address_done:
push cs
pop ds
assume ds:code
ret
rcv_mode_2:
and promiscuous_mode,not 3
or promiscuous_mode,2 ;disable broadcasts.
mov min_frame_length,60
and save_bad_frame,not 80h
mov pro,0
jmp short configure_command
rcv_mode_4:
rcv_mode_3:
and promiscuous_mode,not 3 ;clear promiscuous mode.
mov min_frame_length,60
and save_bad_frame,not 80h
mov pro,0
jmp short configure_command
rcv_mode_6:
and promiscuous_mode,not 3
or promiscuous_mode,1 ;set promiscuous mode.
mov min_frame_length,60 ;allow runt. but let it come in as a bad frame
or save_bad_frame,80h ; yes we want bad frames here
mov pro,1
mov pro1,1
;
configure_command:
mov bp,io_addr
;
; Set up configure command block.
;
mov bx, bit_el + GA_Configure ;Set command block for a
call setup_command_block ; configure command. BIT_EL
; means last command.
;
; Copy configure command parameters to configure command block.
;
mov si,offset config_params ;Set DS:SI to parameters and
mov cx, 6 ; copy them to the command
move_config_params:
lodsw
out dx, ax
loop move_config_params
;
; Signal 586 to execute configure command.
;
mov ax, CUC_Start
mov bx, BIT_CNA
call issue_command
clc
ret
;*****************************************************************
; TDR cable tester
; enter with es:di pointing to time int
;*****************************************************************
public do_tdr
do_tdr:
push bp
push es
push di
mov bp,io_addr
;
; Set up configure command block.
;
mov bx, bit_el + GA_TDR ;Set command block for a
call setup_command_block ; TDR command. BIT_EL
; means last command.
; Signal 586 to execute configure command.
;
mov ax, CUC_Start
mov bx, BIT_CNA
call issue_command
lea dx, [bp].@SMB_Ptr ;Move IO frame to the command
mov ax, offset cb ; command block.
out dx, ax
lea dx,[BP].@CB_Status
working:
in ax,dx
test ax,BIT_C ; check status of transmit
jz working
lea dx,[bp].@CB_Parm0
in ax,dx
pop di
pop es
pop bp
test ax,LNK_OK
jz tdr_bad_cable
clc
ret
tdr_bad_cable:
push ax
and ax,TDR_TIME
stosw ; store time
pop ax
mov cl,12
shr ax,cl
and ax,7
mov dh,al
stc
ret
;
; BX = 586 command word
;
setup_command_block:
;
; Setup command block's command, status, and link fields.
;
lea dx, [bp].@SMB_Ptr ;Move IO frame to the command
mov ax, offset cb ; command block.
out dx, ax
lea dx, [bp].@CB_link ;Set command block's link to
out dx, ax ; itself.
lea dx, [bp].@SCB_CBL ;Point SCB command list
out dx, ax ; pointer to command block.
lea dx, [bp].@CB_command ;Set command block's command
mov ax, bx ; word to value passed in
out dx, ax ; BX.
;
; Return with the write pointer set to the command block parameter
; offset.
;
lea dx, [bp].@Write_Ptr ;Set write pointer to command
lea ax, CB.CB_Param0 ; parameter field.
out dx, ax
mov dx, bp ;@Data_Reg
ret
;
; Wait for 586 command register to be clear ( 1 millisecond max)
; Issue command
; Wait for command to complete (500 milliseconds max)
; Acknowledge command complete
;
;
issue_command:
;
; Make sure 586 command in SCB is clear.
;
mov cx, ax
cmd_clear
jnz issue_command_error_exit
mov ax, cx
;
; Put command code in AX into SCB command register and signal the
; 586 to look at it. The cmd_clear macro leaves DX pointing to
; the SCB command register.
;
out dx, ax
issue_ca
wait_for_commad_status_outer_loop:
;
; Wait here for 586 to complete the command. 586 should never take
; more than 500 Milliseconds to complete a command.
;
call readtickcounter
mov cx, ax
lea dx, [bp].@SCB_STAT
wait_for_commad_status:
in ax, dx
cmp ax, bx
je acknowledge_command_complete
call readtickcounter
neg ax
add ax, cx
cmp ax, ten_mils
jb wait_for_commad_status
_586_command_error:
;
; Signal error by pointing AX to error message, and setting
; condition codes.
;
MOV AX, OFFSET _586_not_responding_msg
OR AX, AX
issue_command_error_exit:
RET
acknowledge_command_complete:
;
; Tell the 586 that we know its done by copying the status back to
; the command register's acknowledge fields. BX has status.
;
lea dx, [bp].@SCB_CMD
out dx, ax
issue_ca
;
; Signal no error.
;
xor ax, ax
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
push bp
mov bp,io_addr
cmd_clear ; wait until last command is done processing
disable_board_ints ; shut off board ints
lea dx,[bp].@SCB_CMD
mov ax, RUC_SUSPEND ; suspend recieve unit
out dx, ax
issue_ca
pop bp ; now ok to un-hook interrupts and release
ret ; driver
public reset_interface
reset_interface:
;reset the interface.
assume ds:code
ret
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
extrn count_in_err: near
extrn count_out_err: near
; extrn resource_err_in:word
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
;
; Set BP to the base IO address of bart. Also disable barts ability
; to generate interrupts.
;
mov bp,io_addr
disable_board_ints
;
; Make sure that command unit is clear before reading the status.
;
cmd_clear
;
; Get the SCB status to see why bart interrupted us. Also need to
; copy the status back to the command word to acknowledge the 586
; interrupt. Issue channel attention to get the 586 to realize the
; interrupt aknowledge.
;
; lea dx, [bp].@SCB_RSCERRS
; in ax,dx
; mov word ptr resource_err_in,ax
; lea dx, [bp].@SCB_OVRNERRS
; in ax,dx
; add word ptr resource_err_in,ax
lea dx, [bp].@SCB_STAT
in ax, dx
and ax, ACK_INT_MASK
lea dx, [bp].@SCB_CMD
out dx, ax
issue_ca ; AX is not changed.
;
; Check for frame receive status. Jump if no frames received,
; otherwise processes the receive.
;
test AX, BIT_FR OR BIT_RNR
jz ExitDriverISR
call ProcessRx
ExitDriverISR:
;
; Here if there was a bad receive status. Make sure the receive unit
; of the 586 is running. Read in the SCB status and jump if we need
; to restart the receive unit. We choose to have the jump to restart
; the RU since most of the time we will not need to do this. This
; saves cycle times when exiting without a restart.
;
lea dx, [bp].@SCB_STAT
in ax, dx
test ax, READY
jz restart_RU
drvisr_continue:
enable_board_ints
ret
restart_RU:
deb 160
;
; Need to restart the receive unit of the 586. Set the SCB pointer to
; the receive frame area.
;
lea dx, [bp].@SCB_RFA
mov ax, Receive_Head
out dx, ax
;
; Make sure 586 is in a state to accept a command.
;
cmd_clear
;
; Put the receive unit start command into the SCB command word and
; issue the channel attention.
;
; NOTE: cmd_clear macro leaves DX pointing to the SCB command word.
;
; lea dx, [bp].@SCB_CMD
mov ax, RUC_START
out dx, ax
issue_ca
jmp drvisr_continue
ProcessRx:
;
; Prime the pump that services receives by setting AX to the receive
; head. There could be over 30 packets received.
;
deb 1
mov ax,Receive_Head
CheckNextReceiveStatus:
;
; Save receive head pointer in BX. BX should not be altered during
; this loop.
;
mov bx, ax
;
; Set SMB pointer to the top of the receive frame.
;
lea dx, [bp].@SMB_Ptr
out dx, ax
;
; Get the status for this receive frame. Check to make sure that
; it completed with no errors. If not, then jump to check the
; 586 receive unit.
;
lea dx, [bp].@FD_Status
in ax, dx
mov save_err,1 ; be sure we done accdently report an error
test ax, BIT_C ; command compleat
jz PRx_no
test ax,BIT_OK ; good frame
jnz ProcessRx_1
call count_in_err
mov save_err,0
cmp pro,1
je PRx_bad ; if not in pro-mode lose bad frames and inc error
; call count_in_err
jmp recv_isr_9
PRx_no:
ret
PRx_bad:
mov dh,0
test ax,BIT_S7
jz PR_no_frag
or dh,2
PR_no_frag:
test ax,BIT_S11
jz PR_no_crc
or dh,4
PR_no_crc:
test ax,BIT_S10
jz PR_no_align
or dh,8
PR_no_align:
test ax,BIT_S8
jz PR_no_dma
or dh,16
PR_no_dma:
test ax,BIT_S9
jz PR_no_ru
or dh,32
PR_no_ru:
mov save_err,dh
jmp ProcessRx_1
ProcessRx_1:
deb 2
;
; Set the read pointer to access the LengthTypeField.
;
; NOTE: BX was set to receive head at the beginning of this
; procedure.
;
lea dx, [bp].@Read_Ptr
lea ax, [BX].IPX_LengthTypeField
out dx, ax
mov dx, bp ;@Data_Reg
mov ax,cs
mov es,ax
mov di,offset our_type
mov cx,8
call repinsw
;.286
; rep insb
;
; Get the packet size from the 586 data structure.
;
lea dx, [bp].@RBD_ByteCount
in ax, dx
and ah, 3fh
mov cx, ax
mov di,offset our_type
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1518
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
deb 3
push bx
push cx ; for some reison recv_find trashes cx
mov dh,save_err
call recv_find
mov bp,cs:io_addr ; just in case
pop cx
pop bx
mov ax,es ;is this pointer null?
or ax,di
je recv_isr_9 ;yes - just free the frame.
push es ;remember where the buffer pointer is.
push di
push cx
deb 4
;
; Set the read pointer to access the packet.
;
; NOTE: BX was set to receive head at the beginning of this
; procedure.
;
lea dx, [bp].@Read_Ptr
lea ax, [BX].IPX_DestAddr_HIGH
out dx, ax
mov dx, bp ;@Data_Reg
;
; Now read the packet into es:di
;
call repinsw
; rep insb
pop cx
pop si
pop ds
assume ds:nothing
call recv_copy ;tell them that we copied it.
mov ax,cs ;restore our ds.
mov ds,ax
assume ds:code
mov bp,io_addr ; just in case
deb 5
recv_isr_9:
;
; Clear the current receive frames status.
;
lea dx, [bp].@FD_Status
xor ax, ax
out dx, ax
;
; Set the end of list bit for the current receive frame.
;
lea dx, [bp].@FD_command
mov ax, BIT_EL
out dx, ax
;
; Read in the pointer to the next receive frame.
;
lea dx, [bp].@FD_Link
in ax, dx
;
; Update our local pointer to the receive resources:
;
; Receive_tail <-- Receive head
; Receive_head <-- next receive frame.
;
mov BX, Receive_Head
mov Receive_Tail, BX
mov Receive_Head, AX
mov cx, ax
;
; Clear previous receive frame descriptors end of list bit to free up
; a receive resource. We do this by moving the write pointer to the
; the command word in the previous receive frame and then writing a
; zero to it.
;
lea dx, [bp].@Write_Ptr
lea ax, [bx].FD_command
out dx, ax
mov dx, bp ;@Data_Reg
xor ax, ax
out dx, ax
;
; Check for another receive frame.
;
mov ax, cx
jmp CheckNextReceiveStatus
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
ret
; This procedure is called from the cmd_clear macro after the macro determines
; that we must wait for a command to clear.
;
; MUST NOT TRASH CX OR THE ISSUE_COMMAND PROCEDURE WILL FAIL.
;
cmd_wait:
;enter with dx -> SCB_CMD
;exit with nc if it cleared in one millisecond, cy if not.
call readtickcounter ;Reference clock. die after
mov bx, ax ; 1 ms.
wait_cmd_clear:
in ax, dx ;Read SCB command
or ax, ax ;Wait for command accepted
jz exit
call readtickcounter
neg ax
add ax, bx
cmp ax, one_mil
jb wait_cmd_clear
stc
exit:
ret
;any code after this will not be kept after initialization.
end_resident label byte
public usage_msg
usage_msg db "usage: exp16 [-n] [-d] [-w] <packet_int_no> [<io_addr>]",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for the Intel EtherExpress 16, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
db "Copyright Intel Corp",CR,LF,'$'
using_186_msg db "Using 80[123]86 I/O instructions.",CR,LF,'$'
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
extrn set_recv_isr: near
;enter with si -> argument string, di -> dword to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> argument string, di -> dword to print.
extrn print_number: near
public etopen
etopen:
;initialize the driver.
assume ds:code
;Determine the processor type. The 8088 and 8086 will actually shift ax
;over by 33 bits, while the 80[123]86 use a shift count mod 32.
mov cl,33
mov ax,0ffffh
shl ax,cl
jz not_186
mov is_186,1
mov dx,offset using_186_msg
mov ah,9
int 21h
not_186:
;
; Get board information (Node address, etc.). Returns with BP
; set to the board IO base address (if not errors).
;
call get_system_info
jnz driver_init_error_exit
disable_board_ints
;
; If they're in an 8-bit slot, make sure that they aren't using the slave PIC.
;
cmp _16_not_8_bit_slot,0 ;are they using a 16-bit slot?
jne check_config_exit ;yes -- cool.
mov dx,offset irq_config_error
cmp int_no, 9 ;no - don't let them use the upper IRQs.
jae driver_init_error_exit
check_config_exit:
;
; Initialize the 586 and the 586 data structures.
;
call init_586
jnz driver_init_error_exit
; test iochrdy
call iochrdy_test
;
; Set up Interrupt line, start the receive unit, and Enable barts interrupt.
;
call set_recv_isr
call ru_start
enable_board_ints
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
mov dx,offset end_resident
clc
ret
driver_init_error_exit:
stc
ret
; int number to IRQ translate table.
IRQ_xlat_table LABEL BYTE
DB 00H ;IRQ 0 Invalid IRQ
DB 00H ;IRQ 1 Invalid IRQ
DB 01H ;IRQ 2 Valid IRQ
DB 02H ;IRQ 3 Valid IRQ
DB 03H ;IRQ 4 Valid IRQ
DB 04H ;IRQ 5 Valid IRQ
DB 00H ;IRQ 6 Invalid IRQ
DB 00H ;IRQ 7 Invalid IRQ
DB 00H ;IRQ 8 Invalid IRQ
DB 01H ;IRQ 9 Valid IRQ
DB 05H ;IRQ 10 Valid IRQ
DB 06H ;IRQ 11 Valid IRQ
max_IRQ EQU $-IRQ_xlat_table
io_addresses label word
dw 300h,310h,320h,330h,340h,350h,360h,370h
dw 200h,210h,220h,230h,240h,250h,260h,270h
dw 0
public parse_args
parse_args:
; mov di,offset int_no
; call get_number
mov di,offset io_addr
call get_number
; mov al,int_no ;ensure that it's in our table.
; cmp al,max_IRQ
; ja get_encoded_irq_error_exit
;
; mov bx,offset irq_xlat_table ;encode it into the number bart wants.
; xlat
;
; or al, al ;if zero, bart doesn't want it.
; je get_encoded_irq_error_exit
; mov encoded_int_no,al
;
clc
ret
;get_encoded_irq_error_exit:
; stc
; ret
bnc_connector_msg db "Configured for the BNC connector",CR,LF,'$'
aui_connector_msg db "Configured for the AUI connector",CR,LF,'$'
tpe_connector_msg db "Configured for the twisted-pair connector",CR,LF,'$'
_8_bit_slot_msg db "The board is in an 8-bit slot",CR,LF,'$'
_16_bit_slot_msg db "The board is in a 16-bit slot",CR,LF,'$'
badset_msg db "Set Address Command Failed",CR,LF,'$'
public print_parameters
print_parameters:
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset io_addr
mov dx,offset io_addr_name
call print_number
mov dx,offset bnc_connector_msg ;print the connection type.
cmp connection_type,BNC
je print_parameters_1
mov dx,offset aui_connector_msg
cmp connection_type,AUI
je print_parameters_1
mov dx,offset tpe_connector_msg
print_parameters_1:
mov ah,9
int 21h
mov dx,offset _16_bit_slot_msg ;print the slot size.
cmp _16_not_8_bit_slot,0
jne print_parameters_2
mov dx,offset _8_bit_slot_msg
print_parameters_2:
mov ah,9
int 21h
ret
init_586:
;enter with bp=io_addr, 586 reset.
;
; Set the number of transmit and receive buffers according to the
; amount of RAM on the adapter.
;
MOV AX, Number_of_Tx_Buffers
MOV BX, num_rx_buf_32k
cmp _64K_not_32K,0
je save_number_of_buffers
MOV BX, num_rx_buf_64k
save_number_of_buffers:
MOV TxBufferCount, AX
MOV RxBufferCount, BX
;
; initialize SCP. Move IO frame to top of SCP.
;
LEA DX, [bp].@SMB_Ptr
MOV AX, OFFSET scp
OUT DX, AX
;
; Write initial SCP values:
; bus width 0 = 16 bit 1 = 8 bit.
; ISCP address always found at address 000000.
;
LEA DX, [BP].@SCP_SystemBus
MOV AL, bus_width
OUT DX, AX
LEA DX, [BP].@SCP_ISCP_Ptr_low
XOR AX, AX
OUT DX, AX
LEA DX, [BP].@SCP_ISCP_Ptr_high
OUT DX, AX
;
; Initialize ISCP. Move IO frame to top of ISCP. NOTE: AX falls
; through as zero.
;
LEA DX, [bp].@SMB_Ptr
OUT DX, AX
;
; Write initial ISCP values:
; ISCP BUSY 1 = Indicates 586 is in initialization
; process. 586 sets this byte to 0 when
; initialization is complete.
;
; SCB address Always 000008
;
LEA DX, [BP].@iscp_busy
MOV AX, initialize_586
OUT DX, AX
LEA DX, [BP].@ISCP_SCB_Ptr_low
XOR AX, AX
OUT DX, AX
LEA DX, [BP].@ISCP_SCB_Ptr_high
OUT DX, AX
LEA DX, [BP].@iscp_scb_offset
MOV AX, OFFSET scb
OUT DX, AX
;
; Initialize SCB. Move Write Ptr to top of SCB. NOTE: AX falls
; through as offset to SCB.
;
XOR AX, AX
LEA DX, [BP].@SCB_Status
OUT DX, AX
LEA DX, [BP].@SCB_Command
OUT DX, AX
LEA DX, [BP].@SCB_CommandList
OUT DX, AX
LEA DX, [BP].@SCB_RecBlockList
OUT DX, AX
LEA DX, [BP].@SCB_CRC_Errors
OUT DX, AX
LEA DX, [BP].@SCB_ALN_Errors
OUT DX, AX
LEA DX, [BP].@SCB_RSC_Errors
OUT DX, AX
LEA DX, [BP].@SCB_OVR_Errors
OUT DX, AX
;
; Initialize Receive Block(s). First set head pointer and link from
; SCB to first frame descriptor. NOTE: IO frame points to ISCB.
; The SCB link field can be addresses becasue the SCB is physically
; contiguous to the ISCB.
;
mov ax,offset Receive_Blocks
lea dx, [bp].@SCB_RecBlockList ;IO address of SCB RF link
out dx, ax
;
; Initialize the frame descriptor structures. Move the Write ptr
; the top of the current frame descriptor. AX falls through with the
; offset to the first frame descriptor. DI is used to keep track of
; the address of the current frame descriptor. The frame descriptor,
; receive buffer descriptor, and receive buffer are all contiguous in
; memory, and make up the frame descriptor structure.
;
MOV DI, AX ;DI is current frame descriptor
MOV RECEIVE_HEAD, DI
MOV CX, RxBufferCount ;CX is number of frame
; descriptors to initialize.
init_receive_frames:
LEA DX, [bp].@SMB_Ptr
MOV AX, DI ; descriptor.
OUT DX, AX
;
; Init Frame Descriptor (FD).
;
XOR AX, AX ;Init frame descriptor status
LEA DX, [BP+4000H]
OUT DX, AX ; and command to zero.
LEA DX, [BP+4002H]
OUT DX, AX
MOV AX, RECEIVE_HEAD ;Init frame descriptors link
CMP CX, 1 ; to next frame descriptor.
JE next_receive_link ; If this is the last receive
; block, then set its link to
LEA AX, [DI+SIZE ReceiveBlock] ; the first receive block.
next_receive_link:
LEA DX, [BP+4004H]
OUT DX, AX
LEA AX, [DI+OFFSET RBD_ByteCount] ;Init frame descriptors
LEA DX, [BP+4006H]
OUT DX, AX ; link to its receive buffer descriptor.
;
; Init Receive Buffer Descriptor (RBD).
;
LEA DX, [BP+4008H]
OUT DX, AX ;Init the RBD Actual count to zero.
MOV AX, -1 ;Init the RBD link to next RBD
LEA DX, [BP+400AH]
OUT DX, AX ; to minus 1 (unused link).
LEA AX, [DI+RB_Data] ;Init the RDB link to the data
LEA DX, [BP+400CH]
OUT DX, AX ; buffer.
XOR AX, AX
LEA DX, [BP+400EH]
OUT DX, AX
MOV AX, RxBufferSize OR BIT_EL ;Init the RBD count and status
LEA DX, [BP+8000H]
OUT DX, AX
MOV RECEIVE_TAIL, DI ;Set receive tail pointer.
ADD DI, SIZE ReceiveBlock ;Point DI to next frame
LOOP init_receive_frames ; descriptor.
;
; Initialize the send block structures. Move the Write pointer the
; top of the current send block. AX falls through with address of
; first send block. DI is used to keep track of the address of the
; current send block. The Transmit command block, transmit buffer
; descriptor, and transmit buffer are all contiguous in memory, and
; make up the send block structure.
;
MOV DI, OFFSET send_blocks
MOV CX, TxBufferCount
LEA DX, [BP].@SMB_Ptr
XOR AX, AX
OUT DX, AX
MOV AX, DI
LEA DX, [BP].@SCB_CommandList
OUT DX, AX
init_send_blocks:
LEA DX, [BP].@SMB_Ptr ;Set write pointer to send
MOV AX, DI ; block.
OUT DX, AX
;
; Init Transmit Control Block (TCB).
;
XOR AX, AX ;Init transmit control block
LEA DX, [BP+4000H]
OUT DX, AX ; status to zero.
LEA DX, [BP+4002H]
OUT DX, AX ;Init transmit control block
; command to anything. The
MOV AX, offset send_blocks ; Init transmit control block
CMP CX, 1 ; link to next send block.
JE next_send_link ; If this is the last send
; block, then point it to
LEA AX, [DI+SIZE SendBlock] ; the first block.
next_send_link:
LEA DX, [BP+4004H]
OUT DX, AX
LEA AX, [DI+OFFSET TBD_ByteCount] ;Init transmit control block
LEA DX, [BP+4006H]
OUT DX, AX ; link to Transmit Buffer
; descriptor.
;
; Init Transmit Buffer Descriptor (TBD).
;
LEA DX, [BP+4008H]
OUT DX, AX ;Init Transmit Buffer Descriptor
; byte count to anything
MOV AX, -1 ;Init Transmit Buffer Descriptor
LEA DX, [BP+400AH]
OUT DX, AX ; link to next TBD.
LEA AX, [DI+OFFSET TB_Data] ;Init Transmit Buffer Descriptor
LEA DX, [BP+400CH]
OUT DX, AX ; link to transmit
XOR AX, AX ; buffer. Low part of pointer
LEA DX, [BP+400EH]
OUT DX, AX ; is done first.
ADD DI, SIZE SendBlock ;Point DI to next send block
LOOP init_send_blocks ; and loop to initialize it.
;
; Enable loopback to insure nothing acidently hits the cable while
; the 586 gets initialized.
;
LEA DX, [BP].@Config
IN AX, DX
OR AL, loopback_enable
OUT DX, AL
;
; Free the 586 from reset.
;
CALL free_586_reset
;
; Initialize and configure the 586. Carry flag set means error and
; AX will point to error message.
;
CALL init_cmd
JNZ init_bart_ram_exit
CALL configure_command
JNZ init_bart_ram_exit
CALL diagnose_command
JNZ init_bart_ram_exit
push cs
pop es
mov si,offset rom_address
mov cx,EADDR_LEN
call set_address
jnc sa_ok
mov dx,offset badset_msg
mov ah,0
int 21h
sa_ok:
;
; Disable loopback.
;
LEA DX, [BP].@Config
IN AX, DX
AND AL, NOT loopback_enable
OUT DX, AL
XOR AX, AX
init_bart_ram_exit:
OR AX, AX
RET
; This procedure must wait until the ASIC finishes reset. This will take
; around 240 uSec. This loop will time out after 500 uSec.
;
five_hundred_micros EQU 1194
;
reset_board:
;enter with bp=io_addr
LEA DX, [BP].@EEPROM_Ctrl
MOV AL, ASIC_Reset
OUT DX, AL
XOR AL, AL
OUT DX, AL
;
; Get current tick count. This loop will wait for 500 uSec to pass
; before failing.
;
CALL ReadTickCounter
MOV BX, AX
reset_delay:
PUSH BX
CALL check_for_bart_hardware
POP BX
CMP AX, bart_board_id
JE reset_board_exit
CALL ReadTickCounter
NEG AX
ADD AX, BX
CMP AX, five_hundred_micros
JB reset_delay
MOV AX, OFFSET reset_error
stc
RET
reset_board_exit:
MOV BX, AX
clc
RET
reset_586:
;enter with bp = io_addr
lea dx, [bp].@EEPROM_Ctrl
mov al, _586_Reset
out dx, al
ret
free_586_reset:
;enter with bp = io_addr
lea dx, [bp].@EEPROM_Ctrl
xor al, al
out dx, al
ret
check_for_bart_hardware:
;
; Set DX to the auto-id port.
;
LEA DX, [BP].@ID_Port
;
; BX will have board ID when the loop is done. CX is the loop count.
;
XOR BX, BX
MOV CX, 4
get_board_id_loop:
;
; Init registers for loop. CX needs to be used in the loop, and AH
; could be set from the last loop.
;
PUSH CX
XOR AH, AH
;
; Read ID port. See description above.
;
IN AL, DX
;
; Make nibble ID a shift count in CL.
;
MOV CL, AL
AND CL, 00000011B
SHL CL, 1
SHL CL, 1
;
; Move ID nibble to low order bits of AX, then shift then into place. Put
; the board ID nibble into BX. After four passes, BX will have board ID.
;
SHR AL, 1
SHR AL, 1
SHR AL, 1
SHR AL, 1
SHL AX, CL
OR BX, AX
;
; Recover loop count, and loop.
;
POP CX
LOOP get_board_id_loop
;
; Return board ID in AX.
;
MOV AX, BX
RET
;
; Command #1: Initialize
;
init_cmd:
XOR AX, AX ; Init command
MOV BX, BIT_CX + BIT_CNA ; status
CALL issue_command
RET
diagnose_command:
;
; Set up individual address command block.
;
MOV BX, BIT_EL+GA_diagnose
CALL setup_command_block
;
; Execute the diagnose command.
;
MOV AX, CUC_Start
MOV BX, BIT_CNA
CALL issue_command
;
; Check diagnostics results.
;
LEA DX, [BP].@SMB_Ptr ;Move IO frame to the command
MOV AX, OFFSET cb ; command block.
OUT DX, AX
LEA DX, [BP].@mem_loc_0 ;Read in the diagnose status
IN AX, DX ; word.
;
; Assume 586 failed the test. Set AX to an error message and exit
; with the zero bit cleared.
;
MOV BX, AX
MOV AX, OFFSET _586_diagnostic_failure
TEST BX, 0800H ;Test failure bit. If set,
JNZ diagnose_exit ; then error exit.
XOR AX, AX ;Set AX and zero bit to
; indicate no error.
diagnose_exit:
RET
;
; Command #4: RU_START
;
ru_start:
;
; Set SCBs pointer to receive blocks at head of list.
;
lea dx, [bp].@SCB_RFA ;Set pointer to receive blocks
mov ax, receive_head ; to the head of the receive
out dx, ax ; block list.
;
; Signal 586 to start the receive unit. cmd_clear leaves DX pointing
; to SCB command register.
;
cmd_clear
mov ax, ruc_start
out dx, ax
issue_CA
ret
find_a_board:
mov bx,offset io_addresses
no_bart_here:
mov bp,[bx]
cmp bp,0
je no_barts
inc bx
inc bx
push bx
call reset_board
pop bx
jc no_bart_here
clc
ret
no_barts:
stc
ret
get_system_info:
cmp io_addr,0
jne addr_override
call find_a_board
jnc board_id_ok
jmp bad_or_no_board
addr_override:
MOV bp, io_addr
;
; Next, get the bart board ID. If the ID is not what we expect, then
; exit with error code in AX.
;
call reset_board
jnc board_id_ok
;
; Could search for bart board, but we error exit instead.
;
bad_or_no_board:
MOV AX, OFFSET board_not_installed
JMP SHORT get_system_info_exit
board_id_ok:
mov io_addr,bp
;
; Since the software will be reading the EEPROM during the
; initialization, the 586 needs to be inactive. This is because the
; control lines to the EEPROM are shared between the 586 and this
; software. To keep from getting fouled up, the 586 reset line is
; asserted here (now that we have a base IO address, and we know that
; the hardware is present). The 586 is not released from reset until
; after its data structures are intialized.
;
CALL reset_586
;
; Validate the EEPROM by doing a checksum.
;
CALL check_eeprom
JC get_system_info_exit
;
; Get the amount of memory on the adapter. If carry clear, then AX
; has memory size. Else AX has error code.
;
CALL test_buffer_memory
JC get_system_info_exit
MOV _64K_not_32K,al
;
; Get the connection type. If carry clear, then AL has connection
; type. Otherwise AX has error code.
;
CALL get_connection_type
JC get_system_info_exit
MOV connection_type, AL
;
; Read the Ethernet address out of the EEPROM.
;
MOV AX, ee_ethernet_add_high
CALL read_eeprom
xchg ah,al
MOV word ptr rom_address[0], AX
MOV AX, ee_ethernet_add_mid
CALL read_eeprom
xchg ah,al
MOV word ptr rom_address[2], AX
MOV AX, ee_ethernet_add_low
CALL read_eeprom
xchg ah,al
MOV word ptr rom_address[4], AX
mov ax,ee_int
call read_eeprom
mov cl,ee_shift
shr ax,cl
mov encoded_int_no,al
mov bx,offset irq_xlat_table
mov cl,0
kg:
cmp [bx],al
je found_it
inc bx
inc cl
jmp kg
found_it:
mov int_no,cl
;
; Get the Slot width. If carry clear, then AL has the slot width.
; Otherwise AX has error code.
;
LEA DX, [BP].@Config
IN AL, DX
;
; Slot width is found in the adapters configuration register.
;
and al, slot_width_mask
mov _16_not_8_bit_slot, al
xor ax, AX
get_system_info_exit:
OR AX, AX
RET
;
;
; Return connection type in AL with carry clear. Otherwise return AX with
; unable_to_read_eeprom error code and carry set.
;
;
get_connection_type:
;
; Read the connection address from the EEPROM.
;
MOV AX, connection_address
CALL read_eeprom ;Read_eeprom does not trash DI
;
; Assume AUI type connection. Check connection fields to see if this
; is a BNC connection. Jump if it is BNC.
;
MOV BL, aui
TEST AX, connection_field
JZ get_connection_type_ret
;
; Here if NOT BNC. Must read another EEPROM word to tell if AUI or
; TPE. Assume AUI and then check for TPE.
;
MOV AX, tpe_address
CALL read_eeprom ;Read_eeprom does not trash DI
;
; Check for AUI connection.
;
mov bl,bnc
TEST AX, TPE_type_field
JZ get_connection_type_ret
;
; Here if TPE type connection.
;
MOV BL, TPE
get_connection_type_ret:
MOV AL, BL
CLC
RET
;
;
; Do a checksum on the EEPROM. Retrun carry set and AX pointing to an error
; message if checksum is bad. Otherwise return carry clear.
;
; The checksum is the same as the board ID.
;
check_eeprom:
mov cx, 40h
xor bx, bx
checksum_loop:
mov ax, cx
dec ax
call read_eeprom
add bx, ax
loop checksum_loop
cmp bx, bart_board_id
jne checksum_error
clc
ret
checksum_error:
mov ax, offset eeprom_checksum_error
stc
ret
test_buffer_memory:
;enter with bp=io_addr
;exit with nc if no error, or cy and di -> address in error.
; Set up SI with the maximum number of words to test. If there is an
; error testing the high 32K of memory, then we will restart the
; test at 32K. When the tests pass, the memory size is passed back
; in AX.
;
mov si, 64 * (1024/2)
;
; Warm up the buffer memory with 16 word writes.
;
mov cx, 16
call write_zeros
cmp cs:is_186,0
je restart_memory_tests
jmp short start_memory_tests
restart_memory_tests:
;
; Here if error testing memory and SI is set to 64K buffer size.
; We restart the tests for 32K buffer only. If an error occurs
; with SI set to 32K, then a memory error is reported.
;
mov si, 32 * (1024/2)
start_memory_tests:
;
; Zero RAM. Set write pointer to the base address and then fill the
; bufffer memory with zeros.
;
mov cx, si
call write_zeros
call word_memory_test_pattern
jc buffer_mem_error_exit
cmp cs:is_186,0
je _8088_quick_exit
mov cx, si
call write_zeros
call byte_memory_test_pattern
jc buffer_mem_error_exit
_8088_quick_exit:
;
; Zero RAM. Set write pointer to the base address and then fill the
; bufffer memory with zeros.
;
mov cx, si
call write_zeros
;
; Set _64K_not_32K to the size of the memory that passed diagnostics.
;
cmp si, 32 * (1024/2) ;did we quit at 32K
je buffer_mem_exit ;yes, we only have 32K.
mov _64K_not_32K,1 ;no, we must have 64K.
buffer_mem_exit:
clc
ret
buffer_mem_error_exit:
;
; If error occured, and SI is not set for 32K, then retry tests with
; SI set for 32K. If the error occurs and the size is 32K, then the
; Bart board memory is bad.
;
CMP SI, 32 * (1024/2)
JNE restart_memory_tests
MOV AX, OFFSET buffer_memory_error
STC ;Set carry to indicate error.
RET
write_zeros:
;enter with bp=io_addr, cx=number of zero words to write.
;
; Move write pointer to the beginning of the buffer memory.
;
LEA DX, [BP].@Write_Ptr
XOR AX, AX
OUT DX, AX
;
; Set DX to the data register and write out zeros CX times.
;
mov dx, bp ;@Data_Reg
warm_up:
out dx, ax
loop warm_up
ret
word_memory_test_pattern:
;enter with si = number of words to test.
;
; Set CX to number of words to test. Set BX to beginning og pattern.
;
MOV CX, SI
MOV BX, 1
;
; Set DI to beginning of the buffer.
;
XOR DI, DI
word_inc_pattern:
CALL loop_set_up
IN AX, DX
OR AX, AX
JNE word_memory_test_pattern_error
;
; Write Test Pattern.
;
MOV AX, BX
OUT DX, AX
;
; Increment BX to next test pattern value.
;
ADD BX, 3
;
; Increment DI to next test memory location.
;
ADD DI, 2
LOOP word_inc_pattern
;
; Set Read pointer to beginning of buffer.
;
LEA DX, [BP].@read_ptr
XOR AX, AX
OUT DX, AX
MOV DX, BP ;@Data_Reg
;
; Set CX to number of words to test. Set BX to beginning og pattern.
;
MOV CX, SI
MOV BX, 1
word_check_pattern:
IN AX, DX
CMP AX, BX
JNE word_memory_test_pattern_error
;
; Increment BX to next test pattern value.
;
ADD BX, 3
LOOP word_check_pattern
CLC
RET
word_memory_test_pattern_error:
STC
RET
byte_memory_test_pattern:
;enter with si = number of bytes to test.
;
; Set CX to number of bytes to test. Set BX to beginning of pattern.
;
MOV CX, SI
SHL CX, 1
MOV BL, 1
;
; Set DI to beginning of the buffer.
;
XOR DI, DI
byte_inc_pattern:
CALL loop_set_up
IN AL, DX
OR AL, AL
JNE byte_memory_test_pattern_error
;
; Write Test Pattern.
;
MOV AL, BL
OUT DX, AL
;
; Increment BX to next test pattern value.
;
ADD BL, 3
;
; Increment DI to next test memory location.
;
INC DI
LOOP byte_inc_pattern
;
; Set Read pointer to beginning of buffer.
;
LEA DX, [BP].@read_ptr
XOR AX, AX
OUT DX, AX
MOV DX, BP ;@Data_Reg
;
; Set CX to number of bytes to test. Set BX to beginning of pattern.
;
MOV CX, SI
SHL CX, 1
MOV BL, 1
byte_check_pattern:
IN AL, DX
CMP AL, BL
JNE byte_memory_test_pattern_error
;
; Increment BX to next test pattern value.
;
add bl, 3
loop byte_check_pattern
clc
ret
byte_memory_test_pattern_error:
stc
ret
loop_set_up:
;enter with bp=io_addr, di=buffer address.
;exit with ax=value at di, dx=I/O address to access buffer memory at DI
PUSH DI
MOV AX, DI ;SMB_Ptr must be on 16 byte
AND AX, 0FFE0H
AND DI, 0001FH
LEA DX, [BP].@SMB_Ptr ;Set IO page frame to the
OUT DX, AX
TEST DI, 0010H
JZ loop_set_up_1
ADD DI, @MEM_LOC_0
loop_set_up_1:
AND DI, 0FFEFH
LEA DX, [BP+DI].@MEM_LOC_0 ;Set DX for IO from buffer and
POP DI
RET
read_eeprom:
;enter with bp=io_addr, ax = EEPROM location.
;exit with ax = EEPROM contents.
;preserves di.
push di
push bx
push cx
; Point to EEPROM control port.
lea dx, [bp].@eeprom_ctrl
mov bx, ax
; Select the EEPROM. Mask off the ASIC and 586 reset bits and set
; the ee_cs bit in the EEPROM control register.
in al, dx
and al, 10110010b
or al, ee_cs
out dx, al
; Write out read opcode and EEPROM location.
mov ax, eeprom_read_opcode ;Set AX to READ opcode and
mov cx, 3 ;Send it to the EEPROM circuit
call shift_bits_out
mov ax, bx ;Tell EEPROM which register is
mov cx, 6 ; to be read. 6 bit address.
call shift_bits_out
call shift_bits_in ;AX gets EEPROM register
call eeprom_clean_up ;Leave EEPROM in known state.
pop cx
pop bx
pop di
ret
shift_bits_out:
;enter with ax=data to be shifted, cx=# of bits to be shifted.
push bx
; Data bits are right justified in the AX register. Move the data
; into BX and left justify it. This will cause addresses to to
; be sent to the EEPROM high order bit first.
mov bx, ax
mov ch, 16
sub ch, cl
xchg cl, ch
shl bx, cl
xchg cl, ch
xor ch, ch
; Get the EEPROM control register into AL. Mask of the ASIC asn 586
; reset bits.
in al, dx
and al, 10111111b
; Set or clear DI bit in EEPROM control register based on value of
; data in BX.
out_shift_loop:
and al, not ee_di ;Assume data bit will be zero
rcl bx, 1 ;Is the data bit a one?
jnc out_with_it ;No
or al, ee_di ;Yes
out_with_it:
out dx, al ;Output a 0 or 1 on data pin
; Set up time for data is .4 Microseconds. So to be safe (incase of
; this software is run on a cray), call delay.
mov di, 1
call eeprom_delay
call raise_eeprom_clock ; Clock the data into the EEPROM.
call lower_eeprom_clock
loop out_shift_loop ;Send next bit
and al, not ee_di ;Force data bit to zero
out dx, al ;Output a 0 on data pin
pop bx
ret
shift_bits_in:
;exit with ax = register contents.
push bx
push cx
; BX will receive the 16 bits read from the EEPROM. Data is valid in
; data out bit (DO) when clock is high. There for, this procedure
; raises clock and waits a minimum amount of time. DO is read, and
; clock is lowered.
in al, dx ;Init AL to eeprom control
and al, 10111111b ; register.
xor bx, bx ;Init holding register
mov cx, 16 ;We'll shift in 16 bits
in_shift_loop:
shl bx, 1 ;Adjust holding register for
; next bit
call raise_eeprom_clock
in al, dx
and al, 10111111b
test al, ee_do ;Was the data bit a one?
jz in_eeprom_delay ;No
or bx, 1 ;Yes, reflect data bit state
; in holding register.
in_eeprom_delay:
call lower_eeprom_clock
loop in_shift_loop ;CONTINUE
mov ax, bx ;AX = data
pop cx
pop bx
ret
raise_eeprom_clock:
or al, ee_sk ;Clock the bit out by raising
jmp short eeprom_clock_common
lower_eeprom_clock:
and al, not ee_sk ;Lower ee_sk
eeprom_clock_common:
out dx, al
mov di, ee_tick
call eeprom_delay ;Waste time
ret
; Lower EEPROM chip select and DI.
; Clock EEPROM twice and leave clock low.
eeprom_clean_up:
push ax
in al, dx
and al, 10111111b
and al, not (ee_cs or ee_di)
out dx, al
call raise_eeprom_clock
call lower_eeprom_clock
pop ax
ret
; DI has number of 838 Nanoseconds clock counts
eeprom_delay:
push ax
push bx
push dx
call readtickcounter
mov bx, ax
eeprom_delay_loop:
call readtickcounter
neg ax
add ax, bx
cmp ax, di
jb eeprom_delay_loop
pop dx
pop bx
pop ax
ret
iochrdy_test:
; First test to see if the driver is supposed to run this test.
MOV AX, lock_bit_address
CALL read_eeprom
TEST AX, lock_bit_mask
JNZ iochrdy_test_exit
; Get the configuration register.
LEA DX, [BP].@config
IN AL, DX
; Set the iochrdy test bit with and set iochrdy to late.
OR AL, iochrdy_test_mask+iochrdy_late
CLI
OUT DX, AL
; Test IOCHRDY with IO.
LEA DX, [BP].@mem_loc_0
IN AX, DX
; Read in the results of the test. Save results in BL.
LEA DX, [BP].@config
IN AL, DX
MOV BL, AL
; Turn iochrdy test off.
AND AL, NOT iochrdy_test_mask
OUT DX, AL
STI
; Test results. Exit if IOCHRDY_LATE bit is set correctly.
TEST BL, iochrdy_test_result
JZ iochrdy_test_exit
; Here if test failed. Clear the 16 bit override bit (force 8 bit
; transfers) and print a warning message.
AND AL, NOT _16_bit_override_bit
OUT DX, AL
mov dx, offset iochrdy_problem
mov ah,9
int 21h
iochrdy_test_exit:
RET
iochrdy_problem DB 10,13,"IOCHRDY Problem. Bart forced into 8 Bit Mode.$"
buffer_memory_error DB 10,13,"Memory error on the Bart board$"
reset_error DB 10,13,"ASIC reset failure on Bart board$"
_586_diagnostic_failure DB 10,13,"82586 diagnostic failure on the "
DB "Bart board$"
_586_not_responding_msg DB 10,13,"82586 did not respond to command "
DB "on the Bart board$"
command_unit_not_idle DB 10,13,"82586 command unit is not "
DB "responding on the Bart board$"
invalid_int_no LABEL WORD
invalid_int_number DB 10,13,"Bart board IRQ/Interrupt number "
DB "not specified correctly$"
eeprom_checksum_error DB 10,13,"EEPROM failed checksum$"
Board_Not_Installed DB 10,13,"Bart Board not found$"
irq_config_error DB 10,13,"IRQ selection is for 16 bit slot "
DB "only.$"
code ends
end